package com.gitee.sparta.common.helpers;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.gitee.sparta.core.bean.SpartaBeanMapping;
import com.gitee.sparta.core.bean.SpartaChannel;
import com.gitee.sparta.core.bean.metadata.Metadata;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * <p>
 *      被反射调用的目标方法形参解析
 * </p>
 *
 * @author zyred
 * @since v 0.0.2
 */
public class ExecuteInvokeHelper {

    /**
     * 执行参数的解析
     *
     * @param metadata          元数据信息，该类是插入的时候就进行包装的，里面包含了用户的 body
     * @param spartaChannel     该对象是提供了 ack 等信息
     * @param mapping           标注了 @SpartaListener 的对象与方法的映射
     * @return                  method.invoke 的参数
     * @throws InvocationTargetException    调用目标异常
     * @throws IllegalAccessException       非法访问例外
     */
    public static <V> Object[] parseTargetMethod(Metadata<V> metadata, SpartaChannel spartaChannel,
                                                 SpartaBeanMapping mapping)
            throws InvocationTargetException, IllegalAccessException, JsonProcessingException {

        Method callbackMethod = mapping.getCallbackMethod();
        Class<?>[] parameterTypes = callbackMethod.getParameterTypes();

        if (parameterTypes.length == 0) {
            return new Object[0];
        }

        Object[] params = new Object[parameterTypes.length];
        String serializeJson = JacksonHelper.serialize(metadata.getBody());

        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> pt = parameterTypes[i];

            if (pt.isAssignableFrom(metadata.getBody().getClass())) {
                params[i] = metadata.getBody();
            } else if (pt == String.class) {
                params[i] = serializeJson;
            } else if (Map.class.isAssignableFrom(pt)) {
                params[i] = JsonHelper.getMap(serializeJson, Map.class, Object.class, Object.class);
            } else if (pt == SpartaChannel.class) {
                params[i] = spartaChannel;
            }
        }
        return params;
    }
}
